Data Visualization Post
For portfolio at willdesi.com
library(tidyverse)
library(leaflet)
# below data downloaded from https://www.motherjones.com/politics/2012/12/mass-shootings-mother-jones-full-data/
df = read_csv("Mother Jones - Mass Shootings Database, 1982 - 2023 - Sheet1.csv")
df = df %>% mutate(gender = case_when(
gender %in% "F" ~ "Female",
gender %in% "M" ~ "Male",
TRUE ~ gender))
df = df %>% mutate(race = case_when(race %in% "-" ~ "Unknown",
race %in% "black" ~ "Black",
race %in% "unclear" ~ "Unknown",
TRUE ~ race))
# separate the location column into two columns; city and state. Keep the location column
df = df %>% tidyr::separate(col = location, into = c("city", "state"), sep = ", ", remove = FALSE)
# get the lat and long for the city and state
df = df %>% geocode(city = city, state = state)
# some lat and long values are NA where the original latitude and longitue are not null. So replace the corresponding NA values in lat and long with those in the other columns.
df = df %>% mutate(Char1 = coalesce(lat, latitude)) %>%
mutate(Char2 = coalesce(long, longitude))
# remove unwanted columns and rename columns ilke location...2 to location
df = df %>% select(-latitude, -longitude, -lat, -long) %>%
rename(location = location...2, latitude = Char1, longitude = Char2)
# save this cleaned data
write_csv(df, "mother_jones_mass_shootings_updated.csv")
DT::datatable(df, rownames = F, filter = "top", options = list(pageLength = 2, scrollX = TRUE
))
Maps
m = leaflet(df)
m %>%
setView(lat = 37.09024, lng = -95.712891, zoom = 4) %>%
addProviderTiles("Esri.WorldStreetMap") %>%
#addProviderTiles(providers$CartoDB.Positron) %>%
addCircles(radius = df$fatalities*1000,
fillOpacity = 0.25,
popup = paste0("<strong>Case: </strong>", df$case, "<br>",
"<strong>Date: </strong>", df$date, "<br>",
"<strong>Place: </strong>", df$location, "<br>",
"<strong>Summary: </strong>", df$summary, "<br>",
"<strong>Fatalities: </strong>", df$fatalities, "<br>"))
Boxplots
Data for the plots
df1 = chickwts %>% mutate(sample = str_c("chick_", 1:n()))
my_colors = paletteer::paletteer_d("ggthemes::excel_Headlines")
head(df1)
df1 = chickwts %>% mutate(sample = str_c("chick_", 1:n()))
my_colors = paletteer::paletteer_d("ggthemes::excel_Headlines")
p2 <- ggplot(data = df1, aes(x = feed, y = weight, color = feed)) +
geom_boxplot(outlier.shape = NA) +
geom_jitter()+
ggthemes::scale_color_calc() +
ggthemes::theme_fivethirtyeight() +
labs(title = "Chicken Weights by Feed Type",
x = "", y = "Weight in Grams")
p2

p <- ggplot(data = df1, aes(x = feed, y = weight, color = feed,
text = paste0("Sample: ", sample,
"<br>","Weight: ",
weight, "g"))) +
geom_boxplot(outlier.shape = NA) +
geom_jitter() +
scale_color_manual(values = my_colors) +
theme_classic() +
labs(title = "Chicken Weights by Feed Type",
x = "", y = "Weight in Grams")
ggplotly(p, tooltip = "text") %>%
layout(legend = list(orientation = "h"))
fig <- plot_ly(df1, y = ~weight, color = ~feed, type = "box",
boxpoints = "all", jitter = 0.3, pointpos = -1.8,
text = paste0("Sample: ", df1$sample, "<br>", "Weight: ", df1$weight, "g"),
hoverinfo = 'text')
fig %>%
layout(legend = list(orientation = "h"),
title = list(text = "Chicken Weights by Feed Type"),
yaxis = list(title = "Weight(grams)"))
Bar plot
# we first have to compute summary statistics of the data
df_summary = chickwts %>%
group_by(feed) %>%
summarise(mean = mean(weight),
sd = sd(weight, na.rm = TRUE))
ggplot(df_summary, aes(x = feed, y = mean)) +
geom_col(color = "black", fill = my_colors) +
geom_errorbar(aes(ymin = mean, ymax=mean + sd), width = 0.2) +
labs(title = "Chicken Weights by Feed Type",
x = "", y = "Weight in Grams")

Violin Plots
# starting with a ggplot object
g <- ggplot(data = df1, aes(x = feed, y = weight, color = feed)) +
scale_color_manual(values = my_colors) +
scale_fill_manual(values = my_colors)
g2 = g +
geom_violin(
aes(fill = feed, fill = after_scale(colorspace::lighten(fill, .5))), size = 1) +
geom_boxplot(
fill = "white", size = 1, width = .2, outlier.shape = NA, coef = 0) +
geom_point(
position = position_jitter(width = .03, seed = 0), size = 2, alpha = .5) +
geom_point(
position = position_jitter(width = .03, seed = 0), size = 2, stroke = .7, shape = 1, color = "black") +
theme(legend.position = "none") +
labs(title = "Chicken Weights by Feed Type", x = "", y = "Weight in Grams")
g2

fig <- df1 %>% plot_ly(x = ~feed, y = ~weight, split = ~feed, type = 'violin', box = list(visible = T), points = "all",
text = ~paste("Sample : ", sample , "<br>Weight: ", weight, "g")) %>%
layout(legend = list(orientation = "h"),
title = list(text = "Chicken Weights by Feed Type"),
yaxis = list(title = "Weight in Grams"))
fig
Raincloud Plots
library(PupillometryR)
my_colors = paletteer::paletteer_d("ggthemes::excel_Headlines")
gg_rain = ggplot(data = df1, aes(x = feed, y = weight, fill = feed, color = feed)) +
PupillometryR::geom_flat_violin(position = position_nudge(x = .2), alpha = .4, trim=FALSE) +
gghalves::geom_half_point(side = "l", range_scale = .3, alpha = .5, size = 3) +
geom_boxplot( width = .2, size = 1, outlier.shape = NA, fill = "white",) +
coord_flip() +
scale_color_manual(values = my_colors) +
scale_fill_manual(values = my_colors) +
ggthemes::theme_calc() +
theme(legend.position = "none") +
labs(title = "Chicken Weights by Feed Type", x = "", y = "Weight in Grams")
gg_rain

g3 = df1 %>% ggplot(aes(x = feed, y = weight, fill = feed)) +
# the half violin
ggdist::stat_halfeye(
adjust = .5, ## bandwidth
.width = c(0.66, 0.95),
position = position_nudge(x = .2), # move geom to the right
color = NA, # removes the slab interval
scale = 0.5
) +
# the boxplot
geom_boxplot(width = .2, size = 1, outlier.color = NA, alpha = 0.5) +
# the dots
ggdist::stat_dots(position = "dodge", scale = 0.5, side = "left", dotsize = 1, justification = 1.2) +
ggthemes::theme_calc() +
theme(legend.position = "none") +
scale_fill_manual(values = my_colors) +
labs(title = "Chicken Weights by Feed Type", x = "", y = "Weight in Grams") +
coord_flip()
g3

LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IA0KICBodG1sX25vdGVib29rOg0KICAgICAgY29kZS1mb2xkOiB0cnVlDQogICAgICBjb2RlLW92ZXJmbG93OiB3cmFwDQogICAgICBlbWJlZC1yZXNvdXJjZXM6IHRydWUNCmV4ZWN1dGU6DQogIHdhcm5pbmc6IGZhbHNlICAgICAgDQotLS0NCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFKQ0KYGBgDQoNCiMgRGF0YSBWaXN1YWxpemF0aW9uIFBvc3QNCg0KRm9yIHBvcnRmb2xpbyBhdCB3aWxsZGVzaS5jb20NCg0KYGBge3J9DQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkobGVhZmxldCkNCg0KIyBiZWxvdyBkYXRhIGRvd25sb2FkZWQgZnJvbSBodHRwczovL3d3dy5tb3RoZXJqb25lcy5jb20vcG9saXRpY3MvMjAxMi8xMi9tYXNzLXNob290aW5ncy1tb3RoZXItam9uZXMtZnVsbC1kYXRhLw0KZGYgPSByZWFkX2NzdigiTW90aGVyIEpvbmVzIC0gTWFzcyBTaG9vdGluZ3MgRGF0YWJhc2UsIDE5ODIgLSAyMDIzIC0gU2hlZXQxLmNzdiIpDQoNCmRmID0gZGYgJT4lIG11dGF0ZShnZW5kZXIgPSBjYXNlX3doZW4oDQogICAgZ2VuZGVyICVpbiUgIkYiIH4gIkZlbWFsZSIsDQogICAgZ2VuZGVyICVpbiUgIk0iIH4gIk1hbGUiLA0KICAgIFRSVUUgfiBnZW5kZXIpKQ0KDQpkZiA9IGRmICU+JSBtdXRhdGUocmFjZSA9IGNhc2Vfd2hlbihyYWNlICVpbiUgIi0iIH4gIlVua25vd24iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmFjZSAlaW4lICJibGFjayIgfiAiQmxhY2siLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmFjZSAlaW4lICJ1bmNsZWFyIiB+ICJVbmtub3duIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiByYWNlKSkNCiMgc2VwYXJhdGUgdGhlIGxvY2F0aW9uIGNvbHVtbiBpbnRvIHR3byBjb2x1bW5zOyBjaXR5IGFuZCBzdGF0ZS4gS2VlcCB0aGUgbG9jYXRpb24gY29sdW1uDQpkZiA9IGRmICU+JSB0aWR5cjo6c2VwYXJhdGUoY29sID0gbG9jYXRpb24sIGludG8gPSBjKCJjaXR5IiwgInN0YXRlIiksIHNlcCA9ICIsICIsIHJlbW92ZSA9IEZBTFNFKQ0KDQojIGdldCB0aGUgbGF0IGFuZCBsb25nIGZvciB0aGUgY2l0eSBhbmQgc3RhdGUNCmRmID0gZGYgJT4lIGdlb2NvZGUoY2l0eSA9IGNpdHksIHN0YXRlID0gc3RhdGUpDQoNCiMgc29tZSBsYXQgYW5kIGxvbmcgdmFsdWVzIGFyZSBOQSB3aGVyZSB0aGUgb3JpZ2luYWwgbGF0aXR1ZGUgYW5kIGxvbmdpdHVlIGFyZSBub3QgbnVsbC4gU28gcmVwbGFjZSB0aGUgY29ycmVzcG9uZGluZyBOQSB2YWx1ZXMgaW4gbGF0IGFuZCBsb25nIHdpdGggdGhvc2UgaW4gdGhlIG90aGVyIGNvbHVtbnMuDQpkZiA9IGRmICU+JSBtdXRhdGUoQ2hhcjEgPSBjb2FsZXNjZShsYXQsIGxhdGl0dWRlKSkgJT4lDQogICAgbXV0YXRlKENoYXIyID0gY29hbGVzY2UobG9uZywgbG9uZ2l0dWRlKSkNCg0KIyByZW1vdmUgdW53YW50ZWQgY29sdW1ucyBhbmQgcmVuYW1lIGNvbHVtbnMgaWxrZSBsb2NhdGlvbi4uLjIgdG8gbG9jYXRpb24NCmRmID0gZGYgJT4lIHNlbGVjdCgtbGF0aXR1ZGUsIC1sb25naXR1ZGUsIC1sYXQsIC1sb25nKSAlPiUNCiAgICByZW5hbWUobG9jYXRpb24gPSBsb2NhdGlvbi4uLjIsIGxhdGl0dWRlID0gQ2hhcjEsIGxvbmdpdHVkZSA9IENoYXIyKQ0KDQojIHNhdmUgdGhpcyBjbGVhbmVkIGRhdGENCndyaXRlX2NzdihkZiwgIm1vdGhlcl9qb25lc19tYXNzX3Nob290aW5nc191cGRhdGVkLmNzdiIpDQpgYGANCg0KDQpgYGB7ciBwYWdlZC5wcmludD1UUlVFfQ0KRFQ6OmRhdGF0YWJsZShkZiwgcm93bmFtZXMgPSBGLCBmaWx0ZXIgPSAidG9wIiwgb3B0aW9ucyA9IGxpc3QocGFnZUxlbmd0aCA9IDIsIHNjcm9sbFggPSBUUlVFDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApKSANCmBgYA0KDQojIyBNYXBzDQoNCmBgYHtyIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTgsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQptID0gbGVhZmxldChkZikNCm0gJT4lDQogICAgc2V0VmlldyhsYXQgPSAzNy4wOTAyNCwgbG5nID0gLTk1LjcxMjg5MSwgem9vbSA9IDQpICU+JQ0KICAgIGFkZFByb3ZpZGVyVGlsZXMoIkVzcmkuV29ybGRTdHJlZXRNYXAiKSAlPiUNCiAgICAjYWRkUHJvdmlkZXJUaWxlcyhwcm92aWRlcnMkQ2FydG9EQi5Qb3NpdHJvbikgJT4lDQogICAgYWRkQ2lyY2xlcyhyYWRpdXMgPSBkZiRmYXRhbGl0aWVzKjEwMDAsIA0KICAgICAgICAgICAgICAgZmlsbE9wYWNpdHkgPSAwLjI1LA0KICAgICAgICAgICAgICAgcG9wdXAgPSBwYXN0ZTAoIjxzdHJvbmc+Q2FzZTogPC9zdHJvbmc+IiwgZGYkY2FzZSwgIjxicj4iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjxzdHJvbmc+RGF0ZTogPC9zdHJvbmc+IiwgZGYkZGF0ZSwgIjxicj4iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjxzdHJvbmc+UGxhY2U6IDwvc3Ryb25nPiIsIGRmJGxvY2F0aW9uLCAiPGJyPiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiPHN0cm9uZz5TdW1tYXJ5OiA8L3N0cm9uZz4iLCBkZiRzdW1tYXJ5LCAiPGJyPiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiPHN0cm9uZz5GYXRhbGl0aWVzOiA8L3N0cm9uZz4iLCBkZiRmYXRhbGl0aWVzLCAiPGJyPiIpKQ0KYGBgDQoNCiMjIEJveHBsb3RzDQoNCkRhdGEgZm9yIHRoZSBwbG90cw0KDQpgYGB7cn0NCmRmMSA9IGNoaWNrd3RzICU+JSBtdXRhdGUoc2FtcGxlID0gc3RyX2MoImNoaWNrXyIsIDE6bigpKSkNCm15X2NvbG9ycyA9IHBhbGV0dGVlcjo6cGFsZXR0ZWVyX2QoImdndGhlbWVzOjpleGNlbF9IZWFkbGluZXMiKQ0KaGVhZChkZjEpDQpgYGANCg0KDQpgYGB7cn0NCnAyIDwtIGdncGxvdChkYXRhID0gZGYxLCBhZXMoeCA9IGZlZWQsIHkgPSB3ZWlnaHQsIGNvbG9yID0gZmVlZCkpICsNCiAgICBnZW9tX2JveHBsb3Qob3V0bGllci5zaGFwZSA9IE5BKSArIA0KICAgIGdlb21faml0dGVyKCkrDQogICAgZ2d0aGVtZXM6OnNjYWxlX2NvbG9yX2NhbGMoKSArDQogICAgZ2d0aGVtZXM6OnRoZW1lX2ZpdmV0aGlydHllaWdodCgpICsNCiAgICBsYWJzKHRpdGxlID0gIkNoaWNrZW4gV2VpZ2h0cyBieSBGZWVkIFR5cGUiLA0KICAgICAgICAgeCA9ICIiLCB5ID0gIldlaWdodCBpbiBHcmFtcyIpDQpwMg0KYGBgDQoNCmBgYHtyfQ0KcCA8LSBnZ3Bsb3QoZGF0YSA9IGRmMSwgYWVzKHggPSBmZWVkLCB5ID0gd2VpZ2h0LCBjb2xvciA9IGZlZWQsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgdGV4dCA9IHBhc3RlMCgiU2FtcGxlOiAiLCBzYW1wbGUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiPGJyPiIsIldlaWdodDogIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHdlaWdodCwgImciKSkpICsNCiAgICBnZW9tX2JveHBsb3Qob3V0bGllci5zaGFwZSA9IE5BKSArIA0KICAgIGdlb21faml0dGVyKCkgKw0KICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBteV9jb2xvcnMpICsNCiAgICB0aGVtZV9jbGFzc2ljKCkgKw0KICAgIGxhYnModGl0bGUgPSAiQ2hpY2tlbiBXZWlnaHRzIGJ5IEZlZWQgVHlwZSIsDQogICAgICAgICB4ID0gIiIsIHkgPSAiV2VpZ2h0IGluIEdyYW1zIikNCg0KZ2dwbG90bHkocCwgdG9vbHRpcCA9ICJ0ZXh0IikgJT4lIA0KICAgIGxheW91dChsZWdlbmQgPSBsaXN0KG9yaWVudGF0aW9uID0gImgiKSkNCmBgYA0KDQpgYGB7cn0NCmZpZyA8LSBwbG90X2x5KGRmMSwgeSA9IH53ZWlnaHQsIGNvbG9yID0gfmZlZWQsIHR5cGUgPSAiYm94IiwNCiAgICAgICAgICAgICAgIGJveHBvaW50cyA9ICJhbGwiLCBqaXR0ZXIgPSAwLjMsIHBvaW50cG9zID0gLTEuOCwgDQogICAgICAgICAgICAgICB0ZXh0ID0gcGFzdGUwKCJTYW1wbGU6ICIsIGRmMSRzYW1wbGUsICI8YnI+IiwgIldlaWdodDogIiwgZGYxJHdlaWdodCwgImciKSwNCiAgICAgICAgICAgICAgIGhvdmVyaW5mbyA9ICd0ZXh0JykNCg0KZmlnICAlPiUgDQogICAgbGF5b3V0KGxlZ2VuZCA9IGxpc3Qob3JpZW50YXRpb24gPSAiaCIpLA0KICAgICAgICAgICB0aXRsZSA9IGxpc3QodGV4dCA9ICJDaGlja2VuIFdlaWdodHMgYnkgRmVlZCBUeXBlIiksDQogICAgICAgICAgIHlheGlzID0gbGlzdCh0aXRsZSA9ICJXZWlnaHQoZ3JhbXMpIikpDQpgYGANCiMjIEJhciBwbG90DQoNCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiMgd2UgZmlyc3QgaGF2ZSB0byBjb21wdXRlIHN1bW1hcnkgc3RhdGlzdGljcyBvZiB0aGUgZGF0YQ0KZGZfc3VtbWFyeSA9IGNoaWNrd3RzICU+JQ0KICAgIGdyb3VwX2J5KGZlZWQpICU+JQ0KICAgIHN1bW1hcmlzZShtZWFuID0gbWVhbih3ZWlnaHQpLA0KICAgICAgICAgICAgICBzZCA9IHNkKHdlaWdodCwgbmEucm0gPSBUUlVFKSkNCg0KZ2dwbG90KGRmX3N1bW1hcnksIGFlcyh4ID0gZmVlZCwgeSA9IG1lYW4pKSArDQogICAgZ2VvbV9jb2woY29sb3IgPSAiYmxhY2siLCBmaWxsID0gbXlfY29sb3JzKSArDQogICAgZ2VvbV9lcnJvcmJhcihhZXMoeW1pbiA9IG1lYW4sIHltYXg9bWVhbiArIHNkKSwgd2lkdGggPSAwLjIpICsNCiAgICBsYWJzKHRpdGxlID0gIkNoaWNrZW4gV2VpZ2h0cyBieSBGZWVkIFR5cGUiLA0KICAgICAgICAgeCA9ICIiLCB5ID0gIldlaWdodCBpbiBHcmFtcyIpDQpgYGANCg0KIyMgVmlvbGluIFBsb3RzDQoNCmBgYHtyfQ0KIyBzdGFydGluZyB3aXRoIGEgZ2dwbG90IG9iamVjdA0KZyA8LSBnZ3Bsb3QoZGF0YSA9IGRmMSwgYWVzKHggPSBmZWVkLCB5ID0gd2VpZ2h0LCBjb2xvciA9IGZlZWQpKSArDQogICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IG15X2NvbG9ycykgKw0KICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IG15X2NvbG9ycykNCg0KZzIgPSBnICsNCiAgICBnZW9tX3Zpb2xpbigNCiAgICAgICAgYWVzKGZpbGwgPSBmZWVkLCBmaWxsID0gYWZ0ZXJfc2NhbGUoY29sb3JzcGFjZTo6bGlnaHRlbihmaWxsLCAuNSkpKSwgc2l6ZSA9IDEpICsNCiAgICBnZW9tX2JveHBsb3QoDQogICAgICAgIGZpbGwgPSAid2hpdGUiLCAgc2l6ZSA9IDEsIHdpZHRoID0gLjIsIG91dGxpZXIuc2hhcGUgPSBOQSwgY29lZiA9IDApICsNCiAgICBnZW9tX3BvaW50KA0KICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX2ppdHRlcih3aWR0aCA9IC4wMywgc2VlZCA9IDApLCBzaXplID0gMiwgYWxwaGEgPSAuNSkgKw0KICAgIGdlb21fcG9pbnQoDQogICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25faml0dGVyKHdpZHRoID0gLjAzLCBzZWVkID0gMCksIHNpemUgPSAyLCBzdHJva2UgPSAuNywgc2hhcGUgPSAxLCBjb2xvciA9ICJibGFjayIpICsNCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsNCiAgICBsYWJzKHRpdGxlID0gIkNoaWNrZW4gV2VpZ2h0cyBieSBGZWVkIFR5cGUiLCB4ID0gIiIsIHkgPSAiV2VpZ2h0IGluIEdyYW1zIikNCiAgICANCmcyDQpgYGANCg0KYGBge3J9DQpmaWcgPC0gZGYxICU+JSBwbG90X2x5KHggPSB+ZmVlZCwgeSA9IH53ZWlnaHQsIHNwbGl0ID0gfmZlZWQsIHR5cGUgPSAndmlvbGluJywgYm94ID0gbGlzdCh2aXNpYmxlID0gVCksIA0KICAgICAgICAgICAgICAgICAgICAgICBtZWFubGluZSA9IGxpc3QodmlzaWJsZSA9IFQpLCANCiAgICAgICAgICAgICAgICAgICAgICAgcG9pbnRzID0gImFsbCIsDQogICAgICAgICAgICAgICAgICAgICAgIHRleHQgPSB+cGFzdGUoIlNhbXBsZSA6ICIsIHNhbXBsZSAsICI8YnI+V2VpZ2h0OiAiLCB3ZWlnaHQsICJnIikpICU+JQ0KICAgIGxheW91dChsZWdlbmQgPSBsaXN0KG9yaWVudGF0aW9uID0gImgiKSwNCiAgICAgICAgICAgdGl0bGUgPSBsaXN0KHRleHQgPSAiQ2hpY2tlbiBXZWlnaHRzIGJ5IEZlZWQgVHlwZSIpLA0KICAgICAgICAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAiV2VpZ2h0IGluIEdyYW1zIikpDQpmaWcNCmBgYA0KIyMgUmFpbmNsb3VkIFBsb3RzDQoNCg0KYGBge3IgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9N30NCmxpYnJhcnkoUHVwaWxsb21ldHJ5UikNCm15X2NvbG9ycyA9IHBhbGV0dGVlcjo6cGFsZXR0ZWVyX2QoImdndGhlbWVzOjpleGNlbF9IZWFkbGluZXMiKQ0KDQpnZ19yYWluID0gZ2dwbG90KGRhdGEgPSBkZjEsIGFlcyh4ID0gZmVlZCwgeSA9IHdlaWdodCwgZmlsbCA9IGZlZWQsIGNvbG9yID0gZmVlZCkpICsNCiAgICBQdXBpbGxvbWV0cnlSOjpnZW9tX2ZsYXRfdmlvbGluKHBvc2l0aW9uID0gcG9zaXRpb25fbnVkZ2UoeCA9IC4yKSwgYWxwaGEgPSAuNCwgdHJpbT1GQUxTRSkgKw0KICAgIGdnaGFsdmVzOjpnZW9tX2hhbGZfcG9pbnQoc2lkZSA9ICJsIiwgcmFuZ2Vfc2NhbGUgPSAuMywgYWxwaGEgPSAuNSwgc2l6ZSA9IDMpICsNCiAgICBnZW9tX2JveHBsb3QoIHdpZHRoID0gLjIsIHNpemUgPSAxLCBvdXRsaWVyLnNoYXBlID0gTkEsIGZpbGwgPSAid2hpdGUiLCkgKw0KICAgIGNvb3JkX2ZsaXAoKSArDQogICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IG15X2NvbG9ycykgKw0KICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IG15X2NvbG9ycykgKw0KICAgIGdndGhlbWVzOjp0aGVtZV9jYWxjKCkgKw0KICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKw0KICAgIGxhYnModGl0bGUgPSAiQ2hpY2tlbiBXZWlnaHRzIGJ5IEZlZWQgVHlwZSIsIHggPSAiIiwgeSA9ICJXZWlnaHQgaW4gR3JhbXMiKQ0KZ2dfcmFpbg0KYGBgDQoNCmBgYHtyfQ0KZzMgPSBkZjEgJT4lIGdncGxvdChhZXMoeCA9IGZlZWQsIHkgPSB3ZWlnaHQsIGZpbGwgPSBmZWVkKSkgKw0KICAgICMgdGhlIGhhbGYgdmlvbGluDQogICAgZ2dkaXN0OjpzdGF0X2hhbGZleWUoDQogICAgYWRqdXN0ID0gLjUsICMjIGJhbmR3aWR0aA0KICAgICAud2lkdGggPSBjKDAuNjYsIDAuOTUpLCANCiAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX251ZGdlKHggPSAuMiksICMgbW92ZSBnZW9tIHRvIHRoZSByaWdodA0KICAgIGNvbG9yID0gTkEsICMgcmVtb3ZlcyB0aGUgc2xhYiBpbnRlcnZhbA0KICAgIHNjYWxlID0gMC41DQogICkgKw0KICAgICMgdGhlIGJveHBsb3QNCiAgICBnZW9tX2JveHBsb3Qod2lkdGggPSAuMiwgc2l6ZSA9IDEsIG91dGxpZXIuY29sb3IgPSBOQSwgYWxwaGEgPSAwLjUpICsNCiAgICAjIHRoZSBkb3RzDQogICAgZ2dkaXN0OjpzdGF0X2RvdHMocG9zaXRpb24gPSAiZG9kZ2UiLCBzY2FsZSA9IDAuNSwgc2lkZSA9ICJsZWZ0IiwgZG90c2l6ZSA9IDEsIGp1c3RpZmljYXRpb24gPSAxLjIpICsNCiAgICBnZ3RoZW1lczo6dGhlbWVfY2FsYygpICsNCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsNCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBteV9jb2xvcnMpICsNCiAgICBsYWJzKHRpdGxlID0gIkNoaWNrZW4gV2VpZ2h0cyBieSBGZWVkIFR5cGUiLCB4ID0gIiIsIHkgPSAiV2VpZ2h0IGluIEdyYW1zIikgKw0KICAgIGNvb3JkX2ZsaXAoKQ0KZzMNCmBgYA0KDQo=